package com.sparrows.pirates.model;

import java.util.ArrayList;
import java.util.List;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;

/**
 * Contains information about a ship.
 * @author krummer
 *
 */
public class Ship {
	private String name; //every ship needs a name, duh.
	private float health;
	private float speed;
	private float maxSpeed;
	private float minSpeed;
	private float acceleration;
	private float range;
	private float damage; //Standard damage without modifiers
	private float reload; //How quickly the cannons are reloaded
	private float reloadCounter; //How much time is left for reloading
	private float fireSpeed; //How fast cannonballs should fly
	private Vector2 position;
	private float rotation;
	private TextureRegion texture;
	private Polygon boundingPoly; //Collision polygon allows for rotation instead of a rectangle
	private List<CannonBall> cannonBalls;
	private List<CannonFireSmoke> cannonSmoke;
	
	public Ship(String name, Vector2 pos, TextureRegion tex) {
		this.name = name;
		this.speed = 1f;
		this.maxSpeed = 2f;
		this.minSpeed = 0.2f; //Use a low speed as the minimum so ship can never fully stop
		this.acceleration = 0.005f;
		this.health = 100.f;
		this.range = 20f;  //preferably in meters :P
		this.damage = 10.f;
		this.fireSpeed = 8f;
		this.reload = 5f;
		this.reloadCounter = this.reload;
		this.rotation = 0f;
		this.position = pos;
		this.texture = tex;
		
		this.cannonBalls = new ArrayList<CannonBall>();
		this.cannonSmoke = new ArrayList<CannonFireSmoke>();
	
		//Create bounding polygon for collision detection
		float[] polyPoints = new float[] {position.x - texture.getRegionWidth()/7, position.y - texture.getRegionHeight()/2,
											position.x + texture.getRegionWidth()/7, position.y - texture.getRegionHeight()/2,
											position.x + texture.getRegionWidth()/7, position.y + texture.getRegionHeight()/4,
											position.x, position.y + texture.getRegionHeight()/2,
											position.x - texture.getRegionWidth()/7, position.y + texture.getRegionHeight()/4};
		this.boundingPoly = new Polygon(polyPoints);
		this.boundingPoly.setOrigin(position.x, position.y);
	}
	
	//Update ship info
	public void update(float dt, List<Rectangle> boundingBoxes, List<Rectangle> bufferZone){
		//Base speed of the ship
		double velX = 0;
		double velY = 0;
		
		//Sailing close to shore slows ship down
		landBufferCollisionCheck(bufferZone);
		
		//Hitting land or rocks completely stops the ship
		if (landCollisionCheck(boundingBoxes) == true){
			
		} else {
			velX = Math.cos(Math.toRadians(rotation+90))*speed;
			velY = Math.sin(Math.toRadians(rotation+90))*speed;
		}
		
		//Update position
		this.position.x += velX;
		this.position.y += velY;
		//Update collision polygon
		this.boundingPoly.translate((float)velX, (float)velY);
		//Check for reload
		if (this.reloadCounter < this.reload) {
			this.reloadCounter += dt;
		}
		
		//Update cannonballs
			for (CannonBall cb : cannonBalls){
				cb.update(dt);
				if (cb.needsDestroying()){
					cannonBalls.remove(cb);
					return;
				}
			}
			for (CannonFireSmoke cfs : cannonSmoke){
				if (cfs.update(dt)) {
					cannonSmoke.remove(cfs);
					return;
				}
			}
	}
	
	//Check for a collision with the terrain
	public boolean landCollisionCheck(List<Rectangle> boundingBoxes){
		float[] points = this.boundingPoly.getTransformedVertices();
		
		for (Rectangle box : boundingBoxes){
			for (int i = 0; i < points.length; i += 2){
				if (box.contains(points[i], points[i+1]))
					return true;
				
			}
		}
		return false;
	}
	
	//Check for a collision with shallow water
	public void landBufferCollisionCheck(List<Rectangle> bufferZone){
		float[] points = this.boundingPoly.getTransformedVertices();
		
		for (Rectangle box : bufferZone){
			for (int i = 0; i < points.length; i += 2){
				if (box.contains(points[i], points[i+1]))
					this.changeSpeed(false, this.acceleration*2);
			}
		}
	}
	
	//Draw ship
	public void draw(SpriteBatch batch){
		//Draw cannonballs
		for (CannonBall cb : cannonBalls){
			cb.draw(batch);
		}
		
		//Draw cannon smoke
				for (CannonFireSmoke cfs : cannonSmoke){
					cfs.draw(batch);
				}
		
		batch.setColor(1,1,1,1);
		//Draw ship
		batch.draw(texture, position.x - texture.getRegionWidth()/2, position.y - texture.getRegionHeight()/2 ,
				texture.getRegionWidth()/2, texture.getRegionHeight()/2,
				texture.getRegionWidth(), texture.getRegionHeight(), 1, 1, rotation);

	}
	
	//Turn the ship
	public void turn(boolean turn, boolean turnLeft){
		if (turn)
			if (turnLeft){
				this.rotation += speed*0.5f;
				this.boundingPoly.rotate(speed*0.5f);
			} else {
				this.rotation -= speed*0.5f;
				this.boundingPoly.rotate(-speed*0.5f);
			}
	}
	
	//Speed up/down
	public void changeSpeed(boolean speedUp){
		if (speedUp)
			this.speed += this.acceleration;
		else
			this.speed -= this.acceleration;
		
		//Clamp speed between min and max values
		if (this.speed >= this.maxSpeed)
			this.speed = this.maxSpeed;
		else if (this.speed <= this.minSpeed)
			this.speed = this.minSpeed;
	}
	//Overload for custom acceleration
	public void changeSpeed(boolean up, float accel){
		if (up)
			this.speed += accel;
		else
			this.speed -= accel;
		
		if (this.speed >= this.maxSpeed)
			this.speed = this.maxSpeed;
		else if (this.speed <= this.minSpeed)
			this.speed = this.minSpeed;
	}
	
	//Fire the cannons
	public void fireCannons(TextureRegion ball, TextureRegion smokeSheet) {
		if (this.reloadCounter >= this.reload){
			float px = this.position.x+2;
			float py = this.position.y;
			
			py += (float)Math.sin(this.rotation)*5;
			
			cannonBalls.add(new CannonBall(ball, new Vector2(px, py), this.fireSpeed, this.range, this.rotation));
			cannonSmoke.add(new CannonFireSmoke(new Vector2(px, py), this.rotation, smokeSheet, 7, 27, 16, 0.05f));
			this.reloadCounter = 0f;
		}
	}
	
	//Getters/Setters
	public void setPosition(Vector2 pos){
		this.position = pos;
	}
	public Vector2 getPosition(){
		return position;
	}
	public void setHealth(float hea){
		this.health = hea;
	}
	public float getHealth(){
		return health;
	}
	public void setRotation(float rot){
		this.rotation = rot;
	}
	public float getRotation(){
		return rotation;
	}
}
